<template>
  <el-card>
    <div class="jend ac">
      <el-button type="primary" :icon="UploadFilled" :loading="deployLoading" @click="handleDeploy">开始部署</el-button>
      <el-button :icon="Tickets" @click="linkDeployHistory">部署历史</el-button>
    </div>
    <div class="mt-12 as">
      <ol>
        <li v-for="item in stepList">
          <div class="jsb ac">
            <div class="mr-16">{{ item.title }}</div>
            <span v-if="item.title == 'upload_file'">{{ parseInt(item.p * 100) }}%</span>
            <el-popover v-if="item.data" placement="right" width="400px" trigger="click">
              <template #reference>
                <el-button type="info" size="small">{{ item.title }}</el-button>
              </template>
              <el-scrollbar height="300px">
                <p>{{ item.data }}</p>
              </el-scrollbar>
            </el-popover>
          </div>
        </li>
      </ol>
    </div>
  </el-card>
</template>

<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { UploadFilled, Tickets } from '@element-plus/icons-vue'
import { confirmDialog, tipsError } from '@/utils/tips'
import { invoke } from '@tauri-apps/api/core'
import { listen } from '@tauri-apps/api/event'
import { basename, dirname } from '@tauri-apps/api/path'
import { Command } from '@tauri-apps/plugin-shell'
import { useLoadingBtn } from '@/hooks/useLoadingBtn'
import { backupProjectFile, compressedFolder } from '@/utils/tauriHandle'
import { cloneObj, guid } from '@/utils/common'
import { deployRecordsStore, projectsStore } from '@/utils/IndexDBWarp'

const props = defineProps({
  row: {
    type: Object,
    default: () => ({})
  },
  serverConfigs: {
    type: Array,
    default: () => []
  }
})

const router = useRouter()
const stepList = ref([])

const log = (title, p, data = '') => {
  const cur = stepList.value.find((x) => x.title == title)
  if (cur) {
    cur.p = p
    cur.data = data
  } else {
    stepList.value.push({ title, p, data })
  }
}

/**执行本地命令 */
const runLocalCommand = async (code, localPath) => {
  const parentDir = await dirname(localPath)
  console.log('parentDir', parentDir)
  const isWindows = /Windows/.test(navigator.userAgent)
  const npmCmd = isWindows ? 'npm-windows' : 'npm'
  const output = await new Command(npmCmd, ['run', code.replace('npm run ', '')], {
    cwd: parentDir
  }).execute()

  if (output.code !== 0) {
    return Promise.reject(output.stderr || '命令执行失败')
  }
  return output.stdout
}

/**根据部署类型整理路径和包名 */
const getRemotePathAndZipName = async (localPath, remotePath, uploadType) => {
  let zipName = await basename(localPath)
  if (uploadType == 1) {
    if (!remotePath.endsWith('.gz')) {
      zipName = await basename(localPath)
      remotePath = remotePath + zipName
    }
  } else if (uploadType == 3) {
    log('目录压缩', 0, `准备压缩文件地址： ${localPath}`)
    const { outPath, gzName, message } = await compressedFolder(localPath).catch((e) => {
      tipsError(e)
      return Promise.reject(e)
    })
    localPath = outPath
    if (!remotePath.endsWith('.gz')) {
      zipName = gzName
      remotePath = remotePath + gzName
    }
    log('目录压缩', 1, message)
  }
  return { localPath, remotePath, zipName }
}

const [handleDeploy, deployLoading] = useLoadingBtn(async () => {
  const row = props.row
  const serverConfig = props.serverConfigs.find((item) => item.id === row.serve)
  if (!serverConfig) {
    tipsError('服务器配置不存在请修改服务器配置')
    return
  }
  await confirmDialog('确定部署吗？')
  log('开始部署', 1)
  const newVersion = row.version + 1

  stepList.value
  let localPath = row.localPath
  let remotePath = row.remotePath.endsWith('/') ? row.remotePath : `${row.remotePath}/`

  if (row.localCommand) {
    log('执行本地命令', 0)
    const msg = await runLocalCommand(row.localCommand, localPath)
    log('执行本地命令', 1, msg)
  }

  const data = await getRemotePathAndZipName(localPath, remotePath, row.uploadType)
  localPath = data.localPath
  remotePath = data.remotePath
  const zipName = data.zipName

  let beforeScript = row.beforeScript
  const deployDir = await dirname(remotePath)
  if (row.backupMethod == 1) {
    let nextIndex = newVersion
    // 整理远程备份命令
    beforeScript += `
      cd ${deployDir}
      if [ -f "${zipName}" ]; then
        echo "文件 ${zipName} 存在，修改文件名为 ${nextIndex}-${zipName} 备份"
        mv ${zipName} ${nextIndex}-${zipName}
      else
        echo "文件 ${zipName} 不存在，可直接上传新的包"
      fi
    `
  }
  let afterScript = row.afterScript
  if (remotePath.endsWith('.gz')) {
    afterScript += `
      cd ${deployDir}
      tar -xzvf ${zipName} --strip-components 1
      `
  }

  const eventId = guid()

  try {
    // 监听事件回调
    const unlisten = await listen(eventId, (event) => {
      const { status, p, data } = event.payload
      log(status, p, data)
    })

    await invoke('upload_file', {
      eventId,
      host: `${serverConfig.host}:${serverConfig.port}`,
      username: serverConfig.username,
      password: serverConfig.password,
      localPath,
      remotePath,
      beforeScript,
      afterScript
    }).finally(() => {
      // 清理监听器
      unlisten()
    })

    let backupFilePath = ''
    if (row.backupMethod == 2) {
      log('本地备份', 0, `备份文件地址： ${localPath}`)
      backupFilePath = await backupProjectFile(row.id, localPath, newVersion)
      log('本地备份', 1, `备份文件地址： ${backupFilePath}`)
    }
    log(row.projectName + '：部署成功', 1)

    // 更新项目版本
    const newRow = cloneObj(row)
    newRow.version = newVersion
    projectsStore.update(newRow)

    // 添加部署记录
    deployRecordsStore.add({
      id: guid(),
      projectId: row.id,
      projectName: row.projectName,
      title: '部署成功', // 后面通过获取git提交记录 TODO
      backupFilePath,
      deployTime: Date.now(),
      version: newVersion
    })
  } catch (error) {
    console.error(error)
    tipsError(error)
  }
})

const linkDeployHistory = () => {
  router.push({
    path: '/deployHistory',
    query: {
      projectId: props.row.id
    }
  })
}
</script>

<style scoped lang="scss"></style>
